Building GoogleTest and GoogleMock directly in a CMake project

您所在的位置:网站首页 gtest gmock Building GoogleTest and GoogleMock directly in a CMake project

Building GoogleTest and GoogleMock directly in a CMake project

#Building GoogleTest and GoogleMock directly in a CMake project| 来源: 网络整理| 查看: 265

UPDATED December 2015:Since the original article was written, gtest and gmock have been merged and moved into a single repository on Github under the name GoogleTest. I’ve updated the content here to reflect the changes and the article now also covers both gtest and gmock. I’ve also revised the general purpose implementation to make it more flexible, expanded its documentation and made it available on Github under a MIT license. I hope you find it useful.

UPDATED September 2019:The generalised implementation was extended further and became the FetchContent module, which was added to CMake in 3.11. The module documentation uses GoogleTest in some of its examples.

Using gtest/gmock with CMake is awesome. Not so awesome is when you don’t have a pre-built gtest/gmock available to use. This article demonstrates a convenient way to add them with automated source download and have them build directly as part of your project using add_subdirectory(). Unlike other common approaches, no manual information has to be provided other than the package to download. The approach is general enough to be applied to any CMake-based external project, not just gtest/gmock.

Before proceeding, I should highlight that if you are only interested in gtest and you do have a pre-built gtest available (e.g. it is provided by the system or you are happy to manually build it outside of your project), then CMake makes it trivial to bring gtest into your project with the find_package() command. See the FindGTest and GoogleTest modules for more information on this approach, which is simple and provides some nice extra features for defining tests.

The conventional approach

I’ll focus for the moment on gtest, since it’s a little simpler than gmock, but the concepts are similar for both. When a fully integrated download and build of gtest is required, typical advice for building it as part of your CMake project is based around using ExternalProject. The gtest library is created as part of your build, but not in a way which makes the CMake targets available to you automatically. This means you end up manually adding additional CMake code to define the libraries, import targets, etc. to make it seem like gtest is a fully integrated part of your build. This is unfortunate, since this is precisely the sort of thing CMake is supposed to be doing for you.

In reality, the typical way ExternalProject is used results in a separate, self-contained sub-build which your CMake project essentially sees as a black box. A simplified version of the regular ExternalProject approach looks something like this (adapted from a more complete example implementation available here, but using a now outdated URL):

include(ExternalProject) ExternalProject_Add(googletest   URL https://googletest.googlecode.com/files/gtest-1.7.0.zip URL_HASH SHA1=f85f6d2481e2c6c4a18539e391aa4ea8ab0394af INSTALL_COMMAND "" ) ExternalProject_Get_Property(googletest binary_dir) add_library(gtest UNKNOWN IMPORTED) add_library(gtest_main UNKNOWN IMPORTED) set_target_properties(gtest PROPERTIES   IMPORTED_LOCATION ${binary_dir}/libgtest.a ) set_target_properties(gtest_main PROPERTIES   IMPORTED_LOCATION ${binary_dir}/libgtest_main.a ) add_dependencies(gtest googletest) add_dependencies(gtest_main googletest)

The annoying part of the above is the need to manually create the gtest and gtest_main import libraries. The above is just assuming a particular platform, etc., but to make this fully general for all platforms, compilers, etc. would make the above considerably more complex. CMake has all the required information already, but it is buried in the external project. What we really want is to have gtest included as part of our build directly, not built as a separate, external project.

Getting CMake to do all the work instead

When used in the normal way, ExternalProject performs its download and build steps during the main project’s build phase, but what we really want is to have the download and unpacking steps performed at configure time (ie when CMake is run) and then pull in the source directory with add_subdirectory() rather than having ExternalProject build it. This would make gtest/gmock a fully integrated part of our build and give us access to all the targets, etc. that CMake already defines for us.

While ExternalProject doesn’t natively allow us to perform the download and unpacking steps at configure time, we can make it do so. We achieve this by invoking ExternalProject as an external build which we perform at configure time. While this sounds convoluted, it is actually relatively straightforward.

We first create a template CMakeLists.txt file to use for the external build (we will use simple examples here which are specific to GoogleTest, but the general implementation provided on Github is set up to support any project). It’s contents look something like this:

cmake_minimum_required(VERSION 2.8.2) project(googletest-download NONE) include(ExternalProject) ExternalProject_Add(googletest   GIT_REPOSITORY https://github.com/google/googletest.git   GIT_TAG main   SOURCE_DIR "${CMAKE_BINARY_DIR}/googletest-src"   BINARY_DIR "${CMAKE_BINARY_DIR}/googletest-build"   CONFIGURE_COMMAND ""   BUILD_COMMAND ""   INSTALL_COMMAND ""   TEST_COMMAND "" )

Assuming the above was saved into a file called CMakeLists.txt.in, we would use it like so in our main project’s CMakeLists.txt:

# Download and unpack googletest at configure time configure_file(CMakeLists.txt.in googletest-download/CMakeLists.txt) execute_process(COMMAND "${CMAKE_COMMAND}" -G "${CMAKE_GENERATOR}" . WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/googletest-download" ) execute_process(COMMAND "${CMAKE_COMMAND}" --build .   WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/googletest-download" ) # Prevent GoogleTest from overriding our compiler/linker options # when building with Visual Studio set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) # Add googletest directly to our build. This adds the following targets: # gtest, gtest_main, gmock and gmock_main add_subdirectory("${CMAKE_BINARY_DIR}/googletest-src"   "${CMAKE_BINARY_DIR}/googletest-build" ) # The gtest/gmock targets carry header search path dependencies # automatically when using CMake 2.8.11 or later. Otherwise we # have to add them here ourselves. if(CMAKE_VERSION VERSION_LESS 2.8.11)   include_directories("${gtest_SOURCE_DIR}/include"   "${gmock_SOURCE_DIR}/include" ) endif() # Now simply link your own targets against gtest, gmock, # etc. as appropriate

A few key features should be noted. The configure_file() command copies our template CMakeLists.txt.in file to the build area and the target file name must be CMakeLists.txt (the add_subdirectory() command requires this). The configure_file() command also does variable substitution, so the actual value of CMAKE_BINARY_DIR will be replaced with the current value when the file is copied. Another key feature is the way we invoke CMake to setup and execute a sub-build of the CMakeLists.txt file we just copied (this is what the two execute_process() commands are doing). These are simply forcing CMake to immediately fully process the CMakeLists.txt file we copied rather than waiting until build time. This is the crucial feature of the technique.

It should be noted that GoogleTest will modify the compiler/linker options internally unless it is explicitly told not to. This is unfortunate, but easily handled by setting the inaccurately named gtest_force_shared_crt option to TRUE (all this does is prevent GoogleTest from modifying options rather than force it to use shared libraries!). Looking through the GoogleTest project reveals that setting BUILD_SHARED_LIBS to TRUE also has the same effect, but this latter option is not specific to GoogleTest and may have other side effects.

With the above, our project now has gtest, gtest_main, gmock and gmock_main targets directly available to it. CMake knows what library file names are relevant, it will use the same build settings as the rest of our project (e.g. build type Debug or Release, which compiler, etc.) and it will work on all platforms, compilers and CMake generators without having to specify anything manually. It will also add the relevant header search path to any target linking against any of these library targets.

CMake also manages the git clone and checkout steps for us and ensures it doesn’t repeat those steps unless it is required, such as if you change the URL of the git repository, change the git tag or delete the build output directory. In short, CMake is doing all the work for you, as it should!

Generalising for any external project

There’s no inherent assumption in the above which restricts this approach to just gtest or gmock. It can be generalised to support any external project which uses CMake as its build system. This essentially amounts to parameterising the project name and the download, source and binary directories inside a CMake function. It can also be made to support more than just git clone/checkout as a download method, it can easily support anything ExternalProject itself supports. I’ve already gone ahead and done all the work for you, so just grab it from the Github project associated with this article. It even includes a simple example with gtest and gmock test cases to show how to use it. Enjoy!

Have a CMake maintainer work on your projectGet the book for more CMake content


【本文地址】


今日新闻


推荐新闻


    CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3